<?php
global $CFG;
require_once($CFG->dirroot . '/blocks/formal_langs/langs_src/tools/JLexPHP/jlex.php');
require_once($CFG->dirroot . '/blocks/formal_langs/tokens_base.php');
require_once($CFG->dirroot . '/blocks/formal_langs/langs_code/system/grammar_pattern_parser.php');
%%

%class grammar_pattern_lexer
%function next_token
%char
%line
%state STRING
%unicode

ALPHA         = [A-Za-z_]
DIGIT         = [0-9]
ALPHA_NUMERIC = {ALPHA}|{DIGIT}
ID            = {ALPHA}({ALPHA_NUMERIC})*
TERMINAL      = [A-Z_]([A-Z_]|[0-9])*
NONTERMINAL   = [a-z_]([a-z_]|[0-9])*
WHITE_SPACE   = ([\ \n\r\t\f])
START         = %start
OPTION_LIST   = (%list|%opt)

%{
    private $token_count = 0;
    
    public $errors = array();

    // buffer for accumulating string
    private $string_buffer;
    // begin and end line for position
    private $string_pos_bl, $string_pos_el;
    // begin and end colon for position
    private $string_pos_bc, $string_pos_ec;

    private function return_token($type, $desc) {
        // create token object
        $res = new block_formal_langs_token_base(NULL, $type, $this->yytext(), $this->return_pos(), $this->token_count);
        $res->set_description($desc);
        // increase token count
        $this->token_count++;

        return $res;
    }

    private function return_pos() {
        $begin_line = $this->yyline;
        $begin_col = $this->yycol;

        $lines = explode("\n", $this->yytext());
        $num_lines = count($lines);

        $end_line = $begin_line + $num_lines - 1;
        $end_col = strlen($lines[$num_lines -1]);

        $res = new block_formal_langs_node_position($begin_line, $end_line, $begin_col, $end_col);
        
        return $res;
    }

    private function return_string() {
        // create position object
        $pos = new block_formal_langs_node_position($this->string_pos_bl, $this->string_pos_el, $this->string_pos_bc, $this->string_pos_ec);
        // create token object
        $res = new block_formal_langs_token_base(NULL, grammar_pattern_yyParser::STRING, $this->string_buffer, $pos, $this->token_count);
        $res->set_description("STRING");
        $this->token_count++;
        $this->string_buffer = '';

        return $res;
    }
%}
                           
%%

<YYINITIAL> {START}       { return $this->return_token(grammar_pattern_yyParser::START, "START"); }
<YYINITIAL> {TERMINAL}    { return $this->return_token(grammar_pattern_yyParser::TERMINAL, "TERMINAL"); }
<YYINITIAL> {NONTERMINAL} { return $this->return_token(grammar_pattern_yyParser::NONTERMINAL, "NONTERMINAL"); }
<YYINITIAL> {ID}          { return $this->return_token(grammar_pattern_yyParser::ID, "ID"); }
<YYINITIAL> {DIGIT}+      { return $this->return_token(grammar_pattern_yyParser::NUMBER, "NUMBER"); }

<YYINITIAL> \" {
    $this->yybegin(self::STRING);
    $this->string_buffer = '';
    $this->string_pos_bl = $this->yyline;
    $this->string_pos_bc = $this->yycol;
 }

<STRING> \\\" { $this->string_buffer .= "\""; }

<STRING> \" {
    $this->string_pos_el = $this->yyline;
    $this->string_pos_ec = $this->yycol + 1;
    $this->yybegin(self::YYINITIAL);
    return $this->return_string();
 }

<STRING> [^\\\"]* { $this->string_buffer .= $this->yytext(); }
<STRING> \\[^\"] { $this->string_buffer .= $this->yytext(); }

<YYINITIAL> \) { return $this->return_token(grammar_pattern_yyParser::RPAREN, "RPAREN"); }
<YYINITIAL> \( { return $this->return_token(grammar_pattern_yyParser::LPAREN, "LPAREN"); }
<YYINITIAL> :  { return $this->return_token(grammar_pattern_yyParser::COLON, "COLON"); }
<YYINITIAL> ;  { return $this->return_token(grammar_pattern_yyParser::SEMICOLON, "SEMICOLON"); }
<YYINITIAL> \| { return $this->return_token(grammar_pattern_yyParser::BAR, "BAR"); }
<YYINITIAL> ,  { return $this->return_token(grammar_pattern_yyParser::COMMA, "COMMA"); }

<YYINITIAL> {OPTION_LIST} { return $this->return_token(grammar_pattern_yyParser::OPTION_LIST, "OPTION_LIST"); }

<YYINITIAL> {WHITE_SPACE} {}

. { /* process bad input */
    $error = array();
    $error['line'] = $this->yyline;
    $error['column'] = $this->yycol;
    $error['input'] = $this->yytext();

    $this->errors[] = $error;
}
